/* 
 * Include standard input/output library for I/O operations
 * Provides functions like printf, sprintf, etc.
 */
#include <stdio.h>

/* 
 * Include standard library for memory allocation, process control, etc.
 * Provides functions like malloc, free, atoi, etc.
 */
#include <stdlib.h>

/* 
 * Include string manipulation library
 * Provides functions like strcpy, strcmp, strlen, etc.
 */
#include <string.h>

/* 
 * Include POSIX API for various system calls
 * Provides functions like getpid, sleep, etc.
 */
#include <unistd.h>

/* 
 * Include system time definitions
 * Provides time-related structures and functions
 */
#include <sys/time.h>

/* 
 * Include time manipulation functions
 * Provides functions like time, localtime, etc.
 */
#include <time.h>

/* 
 * Include custom log management header
 * Provides logging functionality for debug and error messages
 */
#include "log_manage.h"

/* 
 * Include MQTT client header
 * Provides MQTT protocol related definitions and functions
 */
#include "mqtt_client.h"

/* 
 * Define success string constant
 * Used to represent successful operation results
 */
#define SUCCESS "success"

/* 
 * Define application list token constant
 * Used for authentication in app list related operations
 */
#define APP_LIST_TOKEN "132456798798"

/* 
 * Define application status token constant
 * Used for authentication in app status related operations
 */
#define APP_STATUS_TOKEN "123"

/* 
 * Define default timeout value for topic operations in seconds
 * Sets the maximum wait time for MQTT topic responses
 */
#define TOPIC_DEFAULT_TIMEOUT_SEC 15

/* 
 * Enumeration for application information types
 * Defines different categories of app information that can be requested
 */
typedef enum{
    APP_LIST = 0,      /* Request list of all applications */
    APP_STATUS,        /* Request status of specific application */
    APP_INFO_MAX       /* Maximum value for app info types */
}APP_INFO_TYPE;

/* 
 * Static function declaration for getting application message information
 * Internal function that handles the core logic for app info retrieval
 */
static int get_app_message_info(APP_INFO_TYPE info_type,const char *app_name,char *result);

/**
 * @brief  Get timestamp string in formatted format
 * @param  buff: Pointer to buffer where timestamp will be stored
 * @retval Returns 0 on success, -1 on failure
 * This function generates a timestamp string in ISO 8601 format with milliseconds
 */
int get_timestamp(char *buff)
{
    /* Declare variables for time handling */
    time_t t;
    struct tm *t_tm;
    struct timeval tv;
    int usec_3;

    /* Validate input buffer pointer */
    if (NULL == buff)
        return -1;

    /* Get current time with microsecond precision */
    gettimeofday(&tv, NULL);
    /* Convert microseconds to milliseconds (keep only first 3 digits) */
    usec_3 = tv.tv_usec / 1000;

    /* Get current time in seconds since epoch */
    time(&t);
    /* Convert to local time structure */
    t_tm = localtime(&t);

    /* Format timestamp string: YYYY-MM-DDTHH:MM:SS.mmm+0800 */
    sprintf(buff, "%4d-%02d-%02dT%02d:%02d:%02d.%.3d+0800", t_tm->tm_year + 1900, t_tm->tm_mon + 1, t_tm->tm_mday,
            t_tm->tm_hour, t_tm->tm_min, t_tm->tm_sec, usec_3);

    return 0;
}

/* 
 * Function to extract integer value from JSON object
 * @param pVal: Pointer to integer where value will be stored
 * @param pjObject: Pointer to JSON object to search in
 * @param strItem: Name of the JSON item to extract
 * @return TRUE if successful, FALSE otherwise
 * This function safely extracts integer values from JSON objects
 */
uint8_t GetItemInt(int *pVal, const cJSON *pjObject, const char *strItem)
{
    /* Initialize return value to failure */
    uint8_t bRet = FALSE;
    /* Validate input parameters */
    if ((pjObject == NULL) || (strItem == NULL))
        return bRet;
    if (pVal == NULL)
        return bRet;

    /* Declare JSON item pointer and string value buffer */
    cJSON *pjItem = NULL;
    char *strVal = NULL;
    /* Attempt to get the specified JSON item */
    if ((pjItem = cJSON_GetObjectItem(pjObject, strItem)) != NULL)
    {
        /* Extract string value from JSON item */
        if ((strVal = cJSON_GetStringValue(pjItem)) != NULL)
        {
            /* Convert string to integer and store result */
            *pVal = atoi(strVal);
            /* Set return value to success */
            bRet = TRUE;
        }
    }
    return bRet;
}

/* 
 * Function to extract timestamp value from JSON object
 * @param pVal: Pointer to time_t where timestamp will be stored
 * @param pjObject: Pointer to JSON object to search in
 * @param strItem: Name of the JSON item to extract
 * @return TRUE if successful, FALSE otherwise
 * Note: This function currently only validates existence, not conversion
 */
uint8_t GetItemTimeStamp(time_t *pVal, const cJSON *pjObject, const char *strItem)
{
    /* Initialize return value to failure */
    uint8_t bRet = FALSE;
    /* Validate input parameters */
    if ((pjObject == NULL) || (strItem == NULL))
        return bRet;
    if (pVal == NULL)
        return bRet;

    /* Declare JSON item pointer and string value buffer */
    cJSON *pjItem = NULL;
    char *strVal = NULL;
    /* Attempt to get the specified JSON item */
    if ((pjItem = cJSON_GetObjectItem(pjObject, strItem)) != NULL)
    {
        /* Extract string value from JSON item */
        if ((strVal = cJSON_GetStringValue(pjItem)) != NULL)
        {
            /* Currently only validates existence, actual conversion needed */
            bRet = TRUE;
        }
    }
    return bRet;
}

/* 
 * Function to extract string value from JSON object
 * @param pVal: Pointer to buffer where string will be copied
 * @param pjObject: Pointer to JSON object to search in
 * @param strItem: Name of the JSON item to extract
 * @return TRUE if successful, FALSE otherwise
 * This function safely extracts string values from JSON objects
 */
uint8_t GetItemStr(char *pVal, const cJSON *pjObject, const char *strItem)
{
    /* Initialize return value to failure */
    uint8_t bRet = FALSE;
    /* Validate input parameters */
    if ((pjObject == NULL) || (strItem == NULL))
        return bRet;
    if (pVal == NULL)
        return bRet;

    /* Declare JSON item pointer and string value buffer */
    cJSON *pjItem = NULL;
    char *strVal = NULL;

    /* Attempt to get the specified JSON item */
    if ((pjItem = cJSON_GetObjectItem(pjObject, strItem)) != NULL)
    {
        /* Extract string value from JSON item */
        if ((strVal = cJSON_GetStringValue(pjItem)) != NULL)
        {
            /* Copy string value to output buffer */
            strcpy(pVal, strVal);
            /* Set return value to success */
            bRet = TRUE;
        }
    }
    return bRet;
}

/**
 * @brief Execute shell command and capture output
 * @param cmd: Shell command string to execute
 * @param result: Buffer to store command output
 * @return 0 on success, -1 on failure
 * This function executes shell commands and captures their output line by line
 */
int shell_exec(char *cmd, char *result)
{
    /* Initialize return value to failure */
    int ret = -1;
    /* Validate input parameters */
    if (!result || !cmd)
        return ret;

    /* Execute shell command and create file pointer for reading output */
    FILE *fp = popen(cmd, "r");
    /* Buffer for reading command output line by line */
    char buffer[128] = {0};
    /* Track total length of result */
    int len = 0;

    /* Check if command execution was successful */
    if (fp != NULL)
    {
        /* Read command output line by line */
        while (fgets(buffer, sizeof(buffer) - 1, fp) != 0)
        {
            /* Log each line of output for debugging */
            write_log(MSG_DEBUG, "%s\n", buffer);
            /* Append line to result buffer */
            memcpy(result + len, buffer, strlen(buffer));
            /* Update total length counter */
            len += strlen(buffer);
            /* Clear buffer for next read */
            memset(buffer, 0x00, sizeof(buffer));
        }
    }
    else
    {
        /* Log error if command execution failed */
        write_log(MSG_ERROR, "ERROR: shell_exec:error!\n");
        /* Close file pointer and return error */
        pclose(fp);
        return ret;
    }

    /* Close file pointer after reading all output */
    pclose(fp);

    return 0;
}

/* 
 * Static function to escape double quotes in strings for JSON compatibility
 * @param original: Original string that may contain double quotes
 * @param escaped: Buffer where escaped string will be stored
 * This function converts " to \" to make strings JSON-safe
 */
static void escape_double_quotes(const char *original, char *escaped) {
    /* Initialize index for escaped string buffer */
    int j = 0;

    /* Iterate through each character in original string */
    for (int i = 0; original[i] != '\0'; i++) {
        /* Check if current character is a double quote */
        if (original[i] == '"') {
            /* Escape double quote by adding backslash before it */
            escaped[j++] = '\\';
            escaped[j++] = '\"';
        } else {
            /* Copy regular characters without modification */
            escaped[j++] = original[i];
        }
    }

    /* Add null terminator to complete the escaped string */
    escaped[j] = '\0';
}

/* 
 * Static function to send MQTT message and parse response
 * @param app_token: Authentication token for the application
 * @param root: JSON object containing the message to send
 * @param publish_topic: MQTT topic to publish message to
 * @param sub_topic: MQTT topic to subscribe to for response
 * @param result: Buffer to store the parsed result
 * @param timeout: Maximum wait time for response in seconds
 * @return 0 on success, -1 on failure
 * This function handles the complete MQTT request-response cycle
 */
static int message_send_and_parse(const char *app_token,cJSON *root,const char *publish_topic, const char *sub_topic, char *result, int timeout)
{
    /* Initialize variables for return code and string manipulation */
    int ret = 0,len = 0,i = 0;
    /* Buffer for JSON string with escaped quotes */
    char escaped_body[512] = {0};
    /* Buffer for constructing MQTT command */
    char mqtt_cmd[1024] = {0};
    /* Buffer for storing MQTT response */
    char mqtt_result[MESSAGE_MAX_LEN] = {0};

    /* Convert JSON object to string representation */
    char *body = cJSON_Print(root);
    /* Escape double quotes in JSON string for shell command safety */
    escape_double_quotes(body, escaped_body);

    /* Construct mosquitto_rr command for MQTT request-response */
    snprintf(mqtt_cmd, sizeof(mqtt_cmd),
        "mosquitto_rr -h 127.0.0.1 -p 1883 -W %d "
        "-t %s "
        "-m \"%s\" "
        "-e %s "
        "-v "
        "-k 5",
        timeout, publish_topic, escaped_body, sub_topic);
    /* Log the constructed command for debugging */
    write_log(MSG_DEBUG, "cmd: %s\n", mqtt_cmd);
    /* Free memory allocated for JSON string */
    free(body);
    /* Free memory allocated for JSON object */
    cJSON_Delete(root);

    /* Execute the MQTT command and capture result */
    ret = shell_exec(mqtt_cmd, mqtt_result);

    /* Check if command execution failed */
    if (ret)
    {
        /* Print error message and return failure */
        printf("mqtt_cmd shell exec error: %s \n", mqtt_cmd);
        return -1;
    }
    /* Check if command returned empty response */
    else if(strlen(mqtt_result) == 0)
    {
        /* Set timeout error message and return failure */
        strcpy(result, SUB_MESSAGE_TOKEN_TIME_OUT); 
        printf("mqtt_cmd shell exec: %s \n", mqtt_cmd);
        return -1;
    }

    /* Find the start of JSON data in response (skip topic prefix) */
    while (mqtt_result[i] != '{')
    {
        i++;
    }
    /* Calculate total length of response */
    len = strlen(mqtt_result);
    /* Remove topic prefix from response, keeping only JSON data */
    memmove(mqtt_result, mqtt_result + i, len - i);
    /* Add null terminator to truncated string */
    mqtt_result[len - i] = '\0';

    /* Parse the JSON response from MQTT */
    root = cJSON_Parse(mqtt_result);
    /* Check if JSON parsing was successful */
    if (root != NULL) {
        /* Extract relevant fields from JSON response */
        cJSON *ack = cJSON_GetObjectItem(root, "ack");
        cJSON *errmsg = cJSON_GetObjectItem(root, "errmsg");
        cJSON *token = cJSON_GetObjectItem(root, "token");

        /* Validate token matches expected application token */
        if(cJSON_IsString(token) && (strcmp(token->valuestring, app_token) != 0)) {
            /* Log token mismatch for debugging */
            printf("sub token:%s; app token:%s\n",token->valuestring,app_token);
            /* Set token error message */
            strcpy(result, SUB_MESSAGE_TOKEN); 
            /* Mark operation as failed */
            ret = -1;
        }
        /* Check if operation was acknowledged successfully */
        else if (cJSON_IsString(ack) && (strcmp(ack->valuestring, "true") == 0)) {
            /* Copy success message to result buffer */
            strcpy(result, mqtt_result);
        } else {
            /* Check if error message is available */
            if (cJSON_IsString(errmsg)) {
                /* Copy error message to result buffer */
                strcpy(result, errmsg->valuestring);
            }
            /* Mark operation as failed */
            ret = -1;
        }
        /* Free memory allocated for parsed JSON */
        cJSON_Delete(root);
    }
    else {
        /* Handle JSON parsing errors */
        printf("cJSON_Parse app message error\n");
        printf("ERR mqtt_result: %s\n", mqtt_result);
        ret = -1;
    }

    return ret;
}

/* 
 * Function to send application installation message via MQTT
 * @param app_name: Name of the application to install
 * @param package_path: File path to the application package
 * @param appsign: Digital signature for application verification
 * @param result: Buffer to store operation result
 * @return 0 on success, -1 on failure
 * This function handles the MQTT communication for app installation
 */
int install_app_message(const char *app_name, const char *package_path, const char* appsign, char *result)
{
    /* Buffers for timestamp and MQTT topics */
    char timestamp[64] = {0};
    char publish_topic[128] = {0};
    char sub_topic[128] = {0};
    /* Application authentication token */
    char app_token[64] = "132456798798";
    int ret = 0;

    /* Generate current timestamp */
    get_timestamp(timestamp);
    /* Construct MQTT publish topic for file verification */
    sprintf(publish_topic, "/sys.appman/%s/S-appmanager/F-VerifyFile", app_name);
    /* Construct MQTT subscribe topic for responses */
    sprintf(sub_topic, "/%s/sys.appman/S-appmanager/F-VerifyFile", app_name);

    /* Create root JSON object for message */
    cJSON *root = cJSON_CreateObject();
    /* Create data sub-object for message payload */
    cJSON *data = cJSON_CreateObject();
    /* Add token field to root object */
    cJSON_AddStringToObject(root, "token", &app_token);
    /* Add timestamp field to root object */
    cJSON_AddStringToObject(root, "time", timestamp);
    /* Add data object to root object */
    cJSON_AddItemToObject(root, "data", data);
    /* Add source application signature to data object */
    cJSON_AddStringToObject(data, "srcAppSign", appsign);
    /* Add application file path to data object */
    cJSON_AddStringToObject(data, "appFilePath", package_path);

    /* Send message and parse response */
    ret = message_send_and_parse(app_token,root,publish_topic,sub_topic,result,TOPIC_DEFAULT_TIMEOUT_SEC);
    
    return ret;
}

/* 
 * Function to send application control message via MQTT
 * @param app_name: Name of the application to control
 * @param control_action: Action to perform (start, stop, etc.)
 * @param appsign: Digital signature for authentication
 * @param result: Buffer to store operation result
 * @return 0 on success, -1 on failure
 * This function handles application control operations like start/stop
 */
int control_app_message(const char *app_name, const char *control_action, const char* appsign, char *result)
{
    /* Buffers for timestamp and MQTT topics */
    char timestamp[64] = {0};
    char publish_topic[128] = {0};
    char sub_topic[128] = {0};
    /* Application authentication token */
    char app_token[64] = "132456798798";
    int ret = 0;

    /* Generate current timestamp */
    get_timestamp(timestamp);
    /* Construct MQTT publish topic for app control */
    sprintf(publish_topic, "/sys.appman/%s/S-appmanager/F-ControlApp", app_name);
    /* Construct MQTT subscribe topic for responses */
    sprintf(sub_topic, "/%s/sys.appman/S-appmanager/F-ControlApp", app_name);

    /* Create root JSON object for message */
    cJSON *root = cJSON_CreateObject();
    /* Create data sub-object for message payload */
    cJSON *data = cJSON_CreateObject();
    /* Add token field to root object */
    cJSON_AddStringToObject(root, "token", &app_token);
    /* Add timestamp field to root object */
    cJSON_AddStringToObject(root, "time", timestamp);
    /* Add data object to root object */
    cJSON_AddItemToObject(root, "data", data);
    /* Add source application signature to data object */
    cJSON_AddStringToObject(data, "srcAppSign", appsign);
    /* Add destination application name to data object */
    cJSON_AddStringToObject(data, "destAppName", app_name);
    /* Add control operation to data object */
    cJSON_AddStringToObject(data, "operate", control_action);

    /* Send message and parse response */
    ret = message_send_and_parse(app_token,root,publish_topic,sub_topic,result,TOPIC_DEFAULT_TIMEOUT_SEC);

    return ret;
}

/* 
 * Public wrapper function to get application list information
 * @param app_name: Name of the requesting application
 * @param result: Buffer to store the application list
 * @return 0 on success, -1 on failure
 * This function retrieves a list of all installed applications
 */
int get_app_list_info(const char *app_name,char *result){
    int ret = 0;
    /* Call internal function with APP_LIST type */
    ret = get_app_message_info(APP_LIST,app_name,result);
    return ret;
}

/* 
 * Public wrapper function to get application status information
 * @param app_name: Name of the application to check status for
 * @param result: Buffer to store the status information
 * @return 0 on success, -1 on failure
 * This function retrieves status information for a specific application
 */
int get_app_status_info(const char *app_name,char *result){
    int ret = 0;
    /* Call internal function with APP_STATUS type */
    ret = get_app_message_info(APP_STATUS,app_name,result);
    return ret;
}

/* 
 * Internal function to handle application information retrieval
 * @param info_type: Type of information to retrieve (list or status)
 * @param app_name: Name of the application
 * @param result: Buffer to store the retrieved information
 * @return 0 on success, -1 on failure
 * This is the core function that handles all app info requests
 */
static int get_app_message_info(APP_INFO_TYPE info_type,const char *app_name,char *result){
    /* Buffers for timestamp, JSON escaping, and MQTT topics */
    char timestamp[64] = {0};
    char escaped_body[512] = {0};
    char publish_topic[128] = {0};
    char sub_topic[128] = {0};
    /* Application authentication token */
    char app_token[64] = {0};
    int ret = 0;

    /* Determine topics and token based on information type */
    if(info_type == APP_LIST){
        /* Configure for application list request */
        sprintf(publish_topic, "/sys.appman/%s/S-appmanager/F-GetAppList", app_name);
        sprintf(sub_topic, "/%s/sys.appman/S-appmanager/F-GetAppList", app_name);
        sprintf(app_token, APP_LIST_TOKEN);
    }
    else if(info_type == APP_STATUS){
        /* Configure for application status request */
        sprintf(publish_topic, "/sys.appman/%s/S-appmanager/F-GetAppStatus", app_name);
        sprintf(sub_topic, "/%s/sys.appman/S-appmanager/F-GetAppStatus", app_name);
        sprintf(app_token, APP_STATUS_TOKEN);
    }
    else
    {
        /* Handle invalid information type */
        printf("get_app_message_info info_type error\n");
        return -1;
    }

    /* Generate current timestamp */
    get_timestamp(timestamp);

    /* Create root JSON object for message */
    cJSON *root = cJSON_CreateObject();
    /* Add token field to root object */
    cJSON_AddStringToObject(root, "token", &app_token);
    /* Add timestamp field to root object */
    cJSON_AddStringToObject(root, "time", timestamp);

    /* Send message and parse response */
    ret = message_send_and_parse(app_token,root,publish_topic,sub_topic,result,TOPIC_DEFAULT_TIMEOUT_SEC);

    return ret;
}

/* 
 * Function to get application rights information via MQTT
 * @param app_name: Name of the application to check rights for
 * @param result: Buffer to store the rights information
 * @return 0 on success, -1 on failure
 * This function retrieves permission and access rights for an application
 */
int get_app_rights_info(const char *app_name,char *result){
    /* Buffers for timestamp and MQTT topics */
    char timestamp[64] = {0};
    char publish_topic[128] = {0};
    char sub_topic[128] = {0};
    /* Application authentication token */
    char app_token[64] = {0};
    int ret = 0;

    /* Construct MQTT publish topic for rights request */
    sprintf(publish_topic, "/sys.appman/%s/S-appmanager/F-GetAppRights", app_name);
    /* Construct MQTT subscribe topic for responses */
    sprintf(sub_topic, "/%s/sys.appman/S-appmanager/F-GetAppRights", app_name);
    /* Set application token */
    sprintf(app_token, "132456798798");

    /* Generate current timestamp */
    get_timestamp(timestamp);

    /* Create root JSON object for message */
    cJSON *root = cJSON_CreateObject();
    /* Create data sub-object for message payload */
    cJSON *data = cJSON_CreateObject();
    /* Add token field to root object */
    cJSON_AddStringToObject(root, "token", &app_token);
    /* Add timestamp field to root object */
    cJSON_AddStringToObject(root, "time", timestamp);
    /* Add data object to root object */
    cJSON_AddItemToObject(root, "data", data);
    /* Add application name to data object */
    cJSON_AddStringToObject(data, "appname", app_name);

    /* Send message and parse response */
    ret = message_send_and_parse(app_token,root,publish_topic,sub_topic,result,TOPIC_DEFAULT_TIMEOUT_SEC);

    return ret;
}